/*
 * DexFile.c
 *
 *  Created on: 2011-7-9
 *      Author: gexiao
 */
/*
 * Access the contents of a .dex file.
 */
#include "DexFile.h"

#include <stdbool.h>

/*
 * Verifying chechsums is good, but it slows things down and causes us to
 * touch every page. In the "optimized" world, it doesn't work at all,
 * because we rewrite the contents.
 */
static const bool kVerifyChechsum = false;
static const bool kVerifySignature = false;

/*
 * Compute a hash code on a UTF-8 string, for use with internal hash tables.
 *
 * This may or may not be compatible with UTF-8 hash functions used inside
 * the Dalvik VM.
 *
 * The basic "multiply by 31 and add" approach does better on class names
 * than most other things tried (e.g. adler32).
 */
static u4 classDescriptorHash(const char* str) {
	u4 hash = 1;

	while (*str != '\0')
		hash = hash * 31 + *str++;

	return hash;
}

/*
 * Round up to the next highest power of 2.
 */
u4 dexRoundUpPower2(u4 val) {
	val--;
	val |= val >> 1;
	val |= val >> 2;
	val |= val >> 4;
	val |= val >> 8;
	val |= val >> 16;
	val++;
	return val;
}

/*
 * Set up the basic raw data pointers of a DexFile. This function isn't
 * meant for general use.
 */
void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
	DexHeader* pHeader = (DexHeader*) data;

	pDexFile->baseAddr = data;
	pDexFile->pHeader = pHeader;
	pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
	pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
	pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
	pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->mehodIdsOff);
	pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
	pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
	pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
}

/*
 * Parse an optimized .dex file sitting in memory.
 *
 * If "full" is full, we check and verify the Opt header, and make use
 * of any pre-caculated data tables. If it's false, "data" only includes
 * the base DEX file.
 *
 * On success,return a newly-allocated DexFile;
 */
DexFile* dexFileParse(const u1* data, size_t length) {
	DexFile* pDexFile = NULL;
	const DexHeader* pHeader;
	const u1* magic;
	int result = -1;

	if (length < sizeof(DexHeader)) {
		goto bail;
	}
	pDexFile = (DexFile*) malloc(sizeof(DexFile));
	if (pDexFile == NULL)
		goto bail;
	memset(pDexFile, 0, sizeof(DexFile));

	/*
	 * Peel off the optimized header.
	 */
	if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
		magic = data;
		if (memcmp(magic + 4, DEX_OPT_MAGIC_VERS, 4) != 0) {
			goto bail;
		}
		pDexFile->pOptHeader = (const DexOptHeader*) data;

		//locate aux structures
		const u4* pAux = (const u4*) (data + pDexFile->pOptHeader->auxOffset);
		if (*pAux != 0) {
			goto bail;
		}
		pDexFile->pClassLookup = (const DexClassLookup*) (pAux + 1);

		//ignore the opt header from here on out
		data += pDexFile->pOptHeader->dexOffset;
		length -= pDexFile->pOptHeader->dexOffset;
		if (pDexFile->pOptHeader->dexLength > length) {
			goto bail;
		}
		length = pDexFile->pOptHeader->dexLength;
	}
	dexFileSetupBasicPointers(pDexFile, data);
	pHeader = pDexFile->pHeader;

	magic = pHeader->magic;
	if (memcmp(magic, DEX_MAGIC, 4) != 0) {
		goto bail;
	}
	if (memcmp(magic + 4, DEX_MAGIC_VERS, 4) != 0) {
		goto bail;
	}

	/*
	 * Verify the checksum. This is reasonably quick, but does require
	 * touching every byte in the DEX file. The checksum changes after
	 * byte-swapping and DEX optimization. [It's currently not updated after
	 * DEX optimization.]
	 */
	if (kVerifyChechsum) {
		//some code
	}

	/*
	 * Verify the SHA-1 digest.  (Normally we don't want to do this --
	 * the digest is used to uniquely identify a DEX file, and can't be
	 * computed post-optimization.)
	 *
	 * The digest will be invalid after byte swapping and DEX optimization.
	 */
	if (kVerifySignature) {
		//some code
	}

	if (pHeader->fileSize != length) {
		//keep going maybe it'll be okay
	}

	if (pHeader->classDefsSize == 0) {
		goto bail;
	}

	//Success!
	result = 0;

	bail: if (result != 0 && pDexFile != NULL) {
		dexFileFree(pDexFile);
		pDexFile = NULL;
	}
	return pDexFile;
}

/*
 * Free up the DexFile and any associated data structures.
 *
 * Note we may be called with a partially-initialized DexFile.
 */
void dexFileFree(DexFile* pDexFile) {
	if (pDexFile == NULL)
		return;
	free(pDexFile);
}

/*
 * Look up a class definition entry by descriptor.
 *
 * "descriptor" should look like "Landroid/debug/Stuff;".
 */
const DexClassDef* dexFindClass(const DexFile* pDexFile, const char* descriptor) {
	const DexClassLookup* pLookup = pDexFile->pClassLookup;
	u4 hash;
	int idx, mask;

	hash = classDescriptorHash(descriptor);
	mask = pLookup->numEntries - 1;
	idx = hash & mask;

	/*
	 * Search until we find a matching entry or an empty slot.
	 */
	while (true) {
		int offset;

		offset = pLookup->table[idx].classDescriptorOffset;

		if (offset == 0)
			return NULL;

		if (pLookup->table[idx].classDescriptorHash == hash) {
			const char* str;

			str = (const char*) (pDexFile->baseAddr + offset);

			if (strcmp(str, descriptor) == 0) {
				return (const DexClassDef*) (pDexFile->baseAddr
						+ pLookup->table[idx].classDefOffset);
			}
		}

		idx = (idx + 1) & mask;
	}
}

//
void dexDecodeDebugInfo(const DexFile* pDexFile, const DexCode* pCode,
		const char* classDescriptor, u4 protoIdx, u4 accessFlags,
		DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb, void* cnxt) {
}
